﻿namespace Trader.DataServices.Yahoo
{
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Net;
    using System.Text;
    using System.Text.RegularExpressions;
    using System.Xml.XPath;
    using BO.Interface;
    using Interface;
    using NUnit.Framework;

    public class DataProvider : IDataProvider
    {
        private static readonly HistoricalData historicalData;
        private static IList<IStock> chachedStocks;
        private SignalsCache signalsCache;

        static DataProvider()
        {
            historicalData = new HistoricalData(@"Data Source=|DataDirectory|\HistoricalData.sdf");
            /*
                        historicalData = new HistoricalData(
                            @"Data Source=d:\Documents\Visual Studio 2008\Projects\AdvancedProgramming\Trader\Trader\Trader.DataServices.Yahoo\HistoricalData.sdf");
            */
        }

        #region IDataProvider Members

        public IList<IStock> Stocks
        {
            get
            {
                if (chachedStocks == null)
                {
                    chachedStocks = InitializeStockList();
                }

                return chachedStocks;
            }
        }

        public IList<ICurrecny> Currecnies
        {
            get { return historicalData.Currencies.Cast<ICurrecny>().ToList().AsReadOnly(); }
        }

        public ISignalsCache SignalsCache
        {
            get
            {
                if (signalsCache == null)
                {
                    signalsCache = new SignalsCache(historicalData);
                }

                return signalsCache;
            }
        }

        #endregion

        public static void UpdateNewStocksQuotes()
        {
            UpdateStocksQuotes(historicalData.Stocks.Where(stock => !stock.Quotes.Any()));
        }

        public static void UpdateStocksQuotes()
        {
            UpdateStocksQuotes(historicalData.Stocks);
        }

        private static void UpdateStocksQuotes(IEnumerable<Stock> stocks)
        {
            foreach (Stock stock in stocks)
            {
                UpdateStockQuotes(stock);
            }
        }

        private static void UpdateStockQuotes(Stock stock)
        {
            Quote lastQuote = stock.Quotes.OrderBy(quote => quote.Date).LastOrDefault();
            DateTime lastQuoteDate;
            if (lastQuote == null)
            {
                lastQuoteDate = new DateTime(2000, 1, 1);
            }
            else
            {
                lastQuoteDate = lastQuote.Date + TimeSpan.FromDays(1);
            }

            IList<Quote> newQuotes = GetStockQuotes(stock, lastQuoteDate, DateTime.Now);
            historicalData.Quotes.InsertAllOnSubmit(newQuotes);
            historicalData.SubmitChanges();
        }

        private static IList<Quote> GetStockQuotes(Stock stock, DateTime fromDate, DateTime toDate)
        {
            string queryString = CreateQueryString(stock, fromDate, toDate);
            var client = new WebClient();

            StringReader csvQuotesReader;
            try
            {
                csvQuotesReader = new StringReader(client.DownloadString(queryString));
            }
            catch
            {
                return new List<Quote>();
            }

            decimal quotesFactor = stock.StockExchange.FactorToCurrency ?? 1M;

            csvQuotesReader.ReadLine();

            var quotes = new List<Quote>();
            string quotesLine;
            while ((quotesLine = csvQuotesReader.ReadLine()) != null)
            {
                string[] quoteValues = quotesLine.Split(',');

                DateTime date = DateTime.Parse(quoteValues[0]);
                decimal high = decimal.Parse(quoteValues[2])*quotesFactor;
                decimal low = decimal.Parse(quoteValues[3])*quotesFactor;
                decimal close = decimal.Parse(quoteValues[4])*quotesFactor;
                decimal volume = decimal.Parse(quoteValues[5]);

                quotes.Add(new Quote
                               {
                                   Closing = close,
                                   Date = date,
                                   High = high,
                                   Low = low,
                                   StockID = stock.ID,
                                   Volume = volume,
                               });
            }

            return quotes;
        }

        private static string CreateQueryString(Stock stock, DateTime fromDate, DateTime toDate)
        {
            var queryBuilder = new StringBuilder("http://ichart.finance.yahoo.com/table.csv");
            queryBuilder.AppendFormat("?s={0}", stock.Symbol);
            if (!string.IsNullOrEmpty(stock.StockExchange.Symbol))
            {
                queryBuilder.AppendFormat(".{0}", stock.StockExchange.Symbol);
            }

            queryBuilder.AppendFormat("&d={0}&e={1}&f={2}", toDate.Month - 1, toDate.Day, toDate.Year);
            queryBuilder.AppendFormat("&a={0}&b={1}&c={2}", fromDate.Month - 1, fromDate.Day, fromDate.Year);

            return queryBuilder.ToString();
        }

        public IList<IStock> InitializeStockList()
        {
            return
                historicalData.Stocks
                    //.Where(stock => stock.Quotes.Where(quote => quote.Volume > 0).Any())
                    .Cast<IStock>().ToList();
        }

        public static void UpdateSplits()
        {
            int todayYear = DateTime.Now.Year%100;
            for (int year = 0; year < todayYear; year++)
            {
                for (int month = 1; month <= 12; month++)
                {
                    IList<Split> splits = GetSplits(year, month);
                    foreach (Split split in splits)
                    {
                        Stock stockSplit =
                            historicalData.Stocks.Where(
                                stock => stock.StockExchange.ID == 2 && stock.Symbol == split.Symbol).FirstOrDefault();
                        if (stockSplit != null)
                        {
                            historicalData.Splits.InsertOnSubmit(new Yahoo.Split
                                                                     {
                                                                         Date = split.Date,
                                                                         Ratio = split.Ratio,
                                                                         Stock = stockSplit
                                                                     });
                        }
                    }
                }
            }
            historicalData.SubmitChanges();
        }

        private static IList<Split> GetSplits(int year, int month)
        {
            var client = new WebClient();

            string pageString =
                client.DownloadString(string.Format("http://biz.yahoo.com/c/{0}/s{1}.html", year.ToString("D2"), month));

            string pageAsXmlString = CreateLegalXml(pageString);

            var stream = new MemoryStream(Encoding.UTF8.GetBytes(pageAsXmlString));
            var document = new XPathDocument(stream);

            var splits = new List<Split>();

            XPathNodeIterator iterator =
                document.CreateNavigator().Select("//table[tr/td/b='Payable']/tr[not(td/b='Payable') and count(td)=8]");
            while (iterator.MoveNext())
            {
                DateTime date;
                try
                {
                    date = DateTime.ParseExact(iterator.Current.SelectSingleNode("td[2]").Value, "MMM d", null);
                }
                catch (Exception e)
                {
                    Console.WriteLine(e);
                    return null;
                }
                if (date.Month < month)
                {
                    date = new DateTime(2000 + year + 1, date.Month, date.Day);
                }
                else
                {
                    date = new DateTime(2000 + year, date.Month, date.Day);
                }

                string symbol = iterator.Current.SelectSingleNode("td[4]").Value;
                decimal ratio = decimal.Parse(iterator.Current.SelectSingleNode("td[6]").Value.Split('-')[0])/
                                decimal.Parse(iterator.Current.SelectSingleNode("td[6]").Value.Split('-')[1]);

                splits.Add(new Split(symbol, date, ratio));
            }

            return splits;
        }

        private static string CreateLegalXml(string pageString)
        {
            var tagName = new Regex(@"<[^\s>]+");
            var tagsRegex = new Regex(@"<[^\!\s>][^>]*>");
            string replace = tagsRegex.Replace(pageString,
                                               match =>
                                               tagName.Match(match.Value) +
                                               (match.Value[match.Value.Length - 2] == '/' ? "/>" : ">"));
            replace = replace.Replace("<link>", string.Empty).Replace("<meta>", string.Empty)
                .Replace("<br>", string.Empty).Replace("<img>", string.Empty).Replace("<p>", string.Empty)
                .Replace("<hr>", string.Empty).Replace("<input>", string.Empty).Replace("&", string.Empty)
                .Replace("<a>", string.Empty).Replace("<a/>", string.Empty).Replace("</a>", string.Empty)
                .Replace(".", string.Empty).Replace("*", string.Empty).Replace("#", string.Empty)
                .Replace("|", string.Empty);

            replace = new Regex("<script>[^<]*</script>").Replace(replace, string.Empty);
            replace = new Regex("<style>[^<]*</style>").Replace(replace, string.Empty);

            replace = new Regex("<html>.+</html>", RegexOptions.Singleline).Match(replace).Value;
            return replace;
        }

        #region Nested type: Split

        private class Split
        {
            public Split(string symbol, DateTime date, decimal ratio)
            {
                Symbol = symbol;
                Date = date;
                Ratio = ratio;
            }

            public string Symbol { get; private set; }
            public DateTime Date { get; private set; }
            public decimal Ratio { get; private set; }
        }

        #endregion
    }

    [TestFixture]
    public class T
    {
        public class a : EqualityComparer<ExchangeRate>
        {
            public override bool Equals(ExchangeRate x, ExchangeRate y)
            {
                return x.Date == y.Date;
            }

            public override int GetHashCode(ExchangeRate obj)
            {
                return obj.GetHashCode();
            }
        }

        [Test]
        public void t()
        {
            /*IList<IStock> stocks = new DataProvider().Stocks;
            int count = stocks.Count;*/
            //            DataProvider.UpdateNewStocksQuotes();
            DataProvider.UpdateNewStocksQuotes();
            DataProvider.UpdateSplits();

            /*var historicalData =
                new HistoricalData(
                    @"Data Source=d:\Documents\Visual Studio 2008\Projects\AdvancedProgramming\Trader\Trader\Trader.DataServices.Yahoo\HistoricalData.sdf");
            using (StreamReader text = File.OpenText(@"C:\Users\Eli\Desktop\dollartoshekesl.csv"))
            {
                string line;
                while ((line = text.ReadLine()) != null)
                {
                    historicalData.ExchangeRates.InsertOnSubmit(new ExchangeRate
                                                                    {
                                                                        Date = DateTime.ParseExact(
                                                                            line.Split(',')[0], "dd.MM.yyyy", null),
                                                                        SourceCurrencyID = 2,
                                                                        TargetCurrencyID = 1,
                                                                        Rate = decimal.Parse(line.Split(',')[1])
                                                                    });
                }
                IList<ExchangeRate> distinct = historicalData.ExchangeRates.Distinct().ToList();

                List<IGrouping<DateTime, ExchangeRate>> list =
                    historicalData.ExchangeRates.GroupBy(rate => rate.Date).Where(
                        exchangeRates => exchangeRates.Count() > 0).ToList();
            }
            historicalData.SubmitChanges();*/
        }

        [Test]
        public void tt()
        {
            var historicalData =
                new HistoricalData(
                    @"Data Source=d:\Documents\Visual Studio 2008\Projects\AdvancedProgramming\Trader\Trader\Trader.DataServices.Yahoo\HistoricalData.sdf");
            using (StreamReader text = File.OpenText(@"C:\Users\Eli\Desktop\Nasdaq100.csv"))
            {
                string line;
                while ((line = text.ReadLine()) != null)
                {
                    historicalData.Stocks.InsertOnSubmit(new Stock
                                                             {
                                                                 StockExchangeID = 2,
                                                                 Symbol = line.Split(',')[0],
                                                                 Name = line.Split(',')[1]
                                                             });
                }
            }
            historicalData.SubmitChanges();
        }
    }

/*
    [TestFixture]
    public class TT
    {
        [Test]
        public void tt()
        {
            string[] files = Directory.GetFiles(@"D:\FU");
            
            

            var data = new HistoricalData(
                    @"Data Source=d:\Documents\Visual Studio 2008\Projects\AdvancedProgramming\Trader\Trader\Trader.DataServices.Yahoo\HistoricalData.sdf");
            foreach (var filePath in files)
            {
                var stockName = filePath.Remove(filePath.Length - 4).Remove(0, 6);
                var stock = data.Stocks.First(stockl => stockl.Name==stockName);

                string line;
                using (var text = File.OpenText(filePath))
                {
                    while((line=text.ReadLine())!=null)
                    {
                        if(string.IsNullOrEmpty( line.Trim()))
                        {
                            continue;
                        }

                        var dateString = line.Split(new[]{"   "},StringSplitOptions.None)[0];
                        var date = DateTime.ParseExact(dateString, "dd-MM-yyyy",null);
                        var resultsStr = line.Split(new[]{"   "},StringSplitOptions.None)[1];
                        var results = int.Parse(resultsStr);
                        data.SearchResults.InsertOnSubmit(new SearchResult{Date = date, Results = results, Stock = stock});
                    }
                    text.Close();
                }
            }
            data.SubmitChanges();
        }


    }
*/
}

//Real
/*D:\Documents\Visual Studio 2008\Projects\AdvancedProgramming\Trader\Trader\Trade
r.DataServices.Yahoo>"C:\Program Files\Microsoft SDKs\Windows\V6.0A\Bin\SqlMetal
.exe" HistoricalData.sdf  /dbml:HistoricalDataClasses.dbml /namespace:Trader.Dat
aServices.Yahoo /pluralize*/

//Fun
/*D:\Documents\Visual Studio 2008\Projects\AdvancedProgramming\Trader\Trader\Trade
r.DataServices.Yahoo>"C:\Program Files\Microsoft SDKs\Windows\V6.0A\Bin\SqlMetal
.exe" HistoricalData.sdf  /dbml:HistoricalDataClassesNew.dbml /namespace:Trader.Dat
aServices.Yahoo /pluralize*/

//"C:\Program Files\Microsoft SDKs\Windows\V6.0A\Bin\SqlMetal.exe" HistoricalData.sdf  /dbml:HistoricalDataClassesNew.dbml /namespace:Trader.DataServices.Yahoo /pluralize